﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using ZoneFiveSoftware.Common.Data;
using ZoneFiveSoftware.Common.Data.Fitness;
using ZoneFiveSoftware.Common.Data.Measurement;
using ZoneFiveSoftware.Common.Visuals;
using ZoneFiveSoftware.Common.Visuals.Fitness;
using System.Runtime.InteropServices;

namespace PolarImport.DeviceImporter
{
    /// <summary>
    /// Main class of the PolarImport Plugin. 
    /// </summary>
    class PolarImporter : IFitnessDevice 
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="PolarImporter"/> class.
        /// </summary>
        /// <param name="Device">The polar device to work with.</param>
        public PolarImporter(Settings.PolarDevice Device)
        {
            this.id = Device.GUID;
            this.device = Device;
            this.name = "Polar - " + Device.Modells; ;
            System.Resources.ResourceManager resourceManager = Properties.Resources.ResourceManager;
            this.image = (System.Drawing.Image)resourceManager.GetObject("Img_Polar_" + this.device.Monitor.ToString());
        } 

        #region IFitnessDevice Member

        /// <summary>
        /// Open a dialog to prompt the user for device configuration (such as COM port) and return the new configuration string.
        /// If the source has not been configured null will be passed into the configurationInfo parameter.
        /// If null is returned, the configuration failed or was cancelled. This will be called the first time a device is selected and
        /// whenever the user clicks the device configuration button.
        /// </summary>
        /// <param name="configurationInfo"></param>
        /// <returns></returns>
        public string Configure(string configurationInfo)
        {
            DeviceConfiguration prompt = new DeviceConfiguration(device);
            DeviceConfigurationInfo info = DeviceConfigurationInfo.Parse(configurationInfo);
            if (info.PolarDevice == 0)
                info.PolarDevice = this.device.Monitor;
            prompt.ConfigInfo = info;
            if (prompt.ShowDialog() == DialogResult.OK)
            {
                return prompt.ConfigInfo.ToString();
            }
            return null;
        }

        /// <summary>
        /// Return the textual description based on the configuration info. Up to two lines of info may be separated by
        /// Environment.Newline. Typically the info will include the Name on the first line, and a description of the configuration
        /// on the second line, eg:
        /// Garmin - Forerunner 201
        /// COM 3
        /// </summary>
        /// <param name="configurationInfo"></param>
        /// <returns></returns>
        public string ConfiguredDescription(string configurationInfo)
        {
            if (configurationInfo == null)
            {
                return this.Name;
            }
            DeviceConfigurationInfo info = DeviceConfigurationInfo.Parse(configurationInfo);
            string firstLine = this.Name + Environment.NewLine;
            string secondLine = info.DownloadConnection.ToString();
            if (info.DownloadConnection == DeviceConnection.IrCOM)
            {
                secondLine += " Port:" + info.ComPort.ToString();
            }
            return firstLine + secondLine;
        }

        #endregion

        #region IDataImporter Member

        public string Name
        {
            get { return name; }
        }

        public Guid Id
        {
            get { return id; }
        }

        public System.Drawing.Image Image
        {
            get { return image; }
        }

        /// <summary>
        /// Perform the import. For file based imports the configuration info contains the file name that was selected.
        /// </summary>
        /// <param name="configurationInfo">Configuration info.</param>
        /// <param name="monitor">An interface to report % complete, status messages, and completion results.</param>
        /// <param name="importResults">An interface to collect the import results.</param>
        /// <returns>
        /// True if the import succeeded, false if it failed.
        /// </returns>
        public bool Import(string configurationInfo, IJobMonitor monitor, IImportResults importResults)
        {
            // Get the configuration of the connection
            DeviceConfigurationInfo info = DeviceConfigurationInfo.Parse(configurationInfo);

            #region Setup Helper Classes

            // Setup some needed classes
            PolarSSetGeneral psg = new PolarSSetGeneral();
            psg.iSize = Marshal.SizeOf(psg);
            PolarSSetMonitorInfo psmi = new PolarSSetMonitorInfo();
            psmi.iSize = Marshal.SizeOf(psmi);

            // Get Upload Connection
            switch (info.UploadConnection)
            {
                case DeviceConnection.IrCOM:
                case DeviceConnection.IrDA:
                case DeviceConnection.IrUSB:
                    psg.iConnection = ConnectionMethod.HRMCOM_CONNECTION_IR;

                    #if (DEBUG)
                    Plugin.AddDebugInfo("Upload-Connection: IR");
                    #endif

                    break;

                case DeviceConnection.UpLink:
                    psg.iConnection = ConnectionMethod.HRMCOM_CONNECTION_UPLINK;

                    #if (DEBUG)
                    Plugin.AddDebugInfo("Upload-Connection: UPLINK");
                    #endif

                    break;

                default:
                    monitor.ErrorText = Properties.Resources.PolarImport_General_UnknownUpConnection;
                    return false;
            }

            // fill in the parameters for general information about connection.
            psg.iMonitorID = 0;
            psg.bConnectionDlg = true;
            psg.bNoCreateWave = true;
            psg.bNoDeleteWave = true;
            psg.bNoPlayWave = true;
            psg.bLoadFromDataFile = false;
            psg.bSaveAsDataFile = false;
            psg.szWaveFile = null;
            psg.szDlgMsg = Properties.Resources.PolarImport_General_ReadMonitorInfo;
            psg.bFixErrors = true;
            psg.iParam = 0;
            psg.lParam = 0;

            #endregion

            #if (DEBUG)
            int version = PolarHRM.GetLibraryVersion();
            Plugin.AddDebugInfo(string.Format("Polar DLL Version: {0}",version));
            #endif

            #region Get Download Parameters 
            
            // Verbindungsparameter ermitteln
            ConnectionParameter connectionPara = new ConnectionParameter();
            string connectionDetail = "";
            switch (info.DownloadConnection)
            {

                case DeviceConnection.IrDA:

                    #if (DEBUG)
                    Plugin.AddDebugInfo("Verbindung mit Uhr aufbauen über IrDA!");
                    #endif

                    connectionPara = ConnectionParameter.HRMCOM_PARAM_IRDA;
                    connectionDetail = "IR";
                    break;

                case DeviceConnection.IrCOM:

                    #if (DEBUG)
                    Plugin.AddDebugInfo("Verbindung mit Uhr aufbauen über COM-Port!");
                    #endif

                    connectionPara = ConnectionParameter.HRMCOM_PARAM_SERIALPORT;
                    connectionDetail = "COM:" + info.ComPort.ToString();                    
                    break;

                case DeviceConnection.IrUSB:

                    #if (DEBUG)
                    Plugin.AddDebugInfo("Verbindung mit Uhr aufbauen über USB!");
                    #endif

                    connectionPara = ConnectionParameter.HRMCOM_PARAM_DIRECT_USB;
                    connectionDetail = "USB";          
                    break;

                case DeviceConnection.SonicLink:

                    #if (DEBUG)
                    Plugin.AddDebugInfo("Verbindung mit Uhr aufbauen über SonicLink");
                    #endif

                    //  Switch to SonicLink ....
                    return ImportUsingSonicLink(monitor, importResults, info, psg, psmi);
                    break;

                default:
                    monitor.ErrorText = Properties.Resources.PolarImport_General_UnknownDownConnection;
                    return false;
            }
            #endregion

            return ImportUsingIR(monitor, importResults, info, psg, psmi, connectionPara, connectionDetail);
        }

        #endregion

        #region ImportFunctions

        private bool ImportUsingIR(IJobMonitor monitor, 
                                   IImportResults importResults, 
                                   DeviceConfigurationInfo info, 
                                   PolarSSetGeneral psg, 
                                   PolarSSetMonitorInfo psmi, 
                                   ConnectionParameter connectionPara, 
                                   string connectionDetail)
        {
            // Using IR. This is only possible for S- and AXN-Series, and for F55 monitors...

            // First Reset the DLL variables
            if (!PolarHRM.ResetIRCommunication(0))
            {
#if (DEBUG)
                Plugin.AddDebugInfo("ERROR: 'ResetIRCommunication' fehlgeschlagen!");
#endif
                // Resetting IR connection was not successful
                monitor.ErrorText = Properties.Resources.PolarImport_General_CouldNotStartIR;
                return false;
            }

            // Prompt the user to place its watch infront of the IR device and start the connection
            if (MessageDialog.Show(string.Format(Properties.Resources.PolarImport_General_ConnectIR, name),
                name, System.Windows.Forms.MessageBoxButtons.OKCancel, System.Windows.Forms.MessageBoxIcon.Information) ==
                System.Windows.Forms.DialogResult.Cancel)
            {
                monitor.ErrorText = Properties.Resources.PolarImport_General_AbortByUser;

#if (DEBUG)
                Plugin.AddDebugInfo("Benutzerabbruch!");
#endif
                return false;  // Abort by user
            }

            // Show the import window and assign the handle to the polar.dll import window
            PolarImportForm ImportWindow = new PolarImportForm();
            ImportWindow.Show(); // Remember to dispose this window, if one of the communication functions fails!
            psg.hOwnerWnd = ImportWindow.Handle;

            // Start the communication:
            if (!PolarHRM.StartIRCommunication(connectionPara, connectionDetail))
            {
                // Starting of the IR Communication was not successful
                switch (connectionPara)
                {
                    case ConnectionParameter.HRMCOM_PARAM_SERIALPORT:
                        monitor.ErrorText = string.Format(Properties.Resources.PolarImport_General_CouldNotStartCOM, connectionDetail);
                        break;

                    case ConnectionParameter.HRMCOM_PARAM_DIRECT_USB:
                        monitor.ErrorText = Properties.Resources.PolarImport_General_CouldNotStartUSB;
                        break;

                    case ConnectionParameter.HRMCOM_PARAM_IRDA:
                        monitor.ErrorText = Properties.Resources.PolarImport_General_CouldNotStartIrDA;
                        break;
                }
#if (DEBUG)
                Plugin.AddDebugInfo("ERROR: 'StartIRCommunication' fehlgeschlagen! ConnectionParameter: " + connectionPara.ToString());
#endif
                PolarHRM.EndIRCommunication(0);
                ImportWindow.Dispose();
                return false;
            }

            // Read general HR monitor information
            if (!PolarHRM.ReadMonitorInfo(psg, psmi))
            {
#if (DEBUG)
                Plugin.AddDebugInfo("ERROR: 'ReadMonitorInfo' fehlgeschlagen!");
#endif
                monitor.ErrorText = Properties.Resources.PolarImport_General_CouldNotReadMonitorInfo;
                PolarHRM.EndIRCommunication(0);
                ImportWindow.Dispose();
                return false; // Reading of monitorinformation failed
            }
            // now the information about the connected monitor is stored in the psmi class!
            // Check if the connected modell matches the choosen one:
            if (info.PolarDevice != psmi.iMonitorInUse)
            {
#if (DEBUG)
                string MonitorName = PolarHRM.GetHRMonitorName(psmi.iMonitorInUse, psmi.iMonitorSubModel);
                Plugin.AddDebugInfo(string.Format("MonitorInUse: {0} ", psmi.iMonitorInUse.ToString()));
                Plugin.AddDebugInfo(string.Format("MonitorSubModel: {0}", psmi.iMonitorSubModel.ToString()));
                Plugin.AddDebugInfo(string.Format("Verbundenes Polar Modell: {0}", MonitorName));
#endif
                // Prompt the user that there is a wrong watch connected, and ask if to continue import.
                // TODO Resource String!
                //if (MessageDialog.Show(string.Format("Connected HR monitor is not a {0}! Try to import anyway?",this.name), "Wrong monitor!", System.Windows.Forms.MessageBoxButtons.YesNo, System.Windows.Forms.MessageBoxIcon.Warning ) ==
                //    System.Windows.Forms.DialogResult.No)
                //{
                //    monitor.ErrorText = Properties.Resources.PolarImport_General_AbortByUser;
                //    return false;  // Abort by user
                //}   
            }
            bool result = false;
            switch (info.PolarDevice)
            {
                case MonitorType.HRM_F55:
                    // Handle F55 IR import
                    monitor.ErrorText = "Not implemented!";
                    //if (!PolarHRM.ReadF55ExerciseFiles(psg.hOwnerWnd))
                    //{
                    //}                    
                    PolarHRM.EndIRCommunication(0);
                    ImportWindow.Dispose();
                    return false;
                default:
                    // Handle S- and AXN import
                    if (!PolarHRM.ReadExercisesData(psg.hOwnerWnd, false))
                    {
#if (DEBUG)
                        Plugin.AddDebugInfo("ERROR: 'ReadExercisesData' fehlgeschlagen!");
#endif
                        monitor.ErrorText = Properties.Resources.PolarImport_General_CouldNotReadExerciseFiles;
                        PolarHRM.SendMonitorToWatchMode(psg);
                        PolarHRM.EndIRCommunication(0);
                        ImportWindow.Dispose();
                        return false;
                    }
                    // Now Analyze all exercise files!
                    result = PolarImportJobs.ProcessSAXNData(info, monitor, importResults, psmi);
#if (DEBUG)
                    if (result == false)
                    {
                        Plugin.AddDebugInfo("ERROR: 'ProcessSAXNData' fehlgeschlagen!");
                    }
#endif
                    break;
            }

            PolarHRM.SendMonitorToWatchMode(psg);
            PolarHRM.EndIRCommunication(0);
            ImportWindow.Dispose();
#if (DEBUG)
            if (result == false)
            {
                Plugin.AddDebugInfo("ERROR: Import nicht erfolgreich!");
            }
            else
            {
                Plugin.AddDebugInfo("Import erfolgreich abgeschlossen!");
            }
#endif
            return result;
        }

        private bool ImportUsingSonicLink(IJobMonitor monitor,
                                          IImportResults importResults,
                                          DeviceConfigurationInfo info,
                                          PolarSSetGeneral psg,
                                          PolarSSetMonitorInfo psmi)
        {

//            PolarHRM.ResetSonicCommunication(0);
//#if (DEBUG)
//            Plugin.AddDebugInfo("Sonic Communication resetet!");
//#endif
            // Benutzer anweißen, wie er seine Polaruhr an das Mikro halten muss...
            if (MessageDialog.Show(string.Format(Properties.Resources.PolarImport_General_PlaceHRMSonicLink, name),
                name, System.Windows.Forms.MessageBoxButtons.OKCancel, System.Windows.Forms.MessageBoxIcon.Information) ==
                System.Windows.Forms.DialogResult.Cancel)
            {
                monitor.ErrorText = Properties.Resources.PolarImport_General_AbortByUser;
#if (DEBUG)
                Plugin.AddDebugInfo("Benutzerabbruch!");
#endif
                return false;  // Abort by user
            }

            // Show the import window and assign the handle to the polar.dll import window
            PolarImportForm ImportWindow = new PolarImportForm();
            ImportWindow.Show(); // Remember to dispose this window, if one of the communication functions fails!
            psg.hOwnerWnd = ImportWindow.Handle;

            monitor.StatusText = Properties.Resources.PolarImport_General_ReadingData;
            monitor.PercentComplete = 0.0f;
#if (DEBUG)
            Plugin.AddDebugInfo("Verbindung mit Uhr:" + info.PolarDevice.ToString());
#endif
            int readdata = PolarHRM.ReadSonicLinkData(ImportWindow.Handle, (int)info.PolarDevice, info.SamplingFrequency, info.Amplification, false);
            switch (readdata)
            {
                case 0: // Cancelled
                    {
                        monitor.StatusText = Properties.Resources.PolarImport_General_ReadingCancelled;
                        monitor.ErrorText = Properties.Resources.PolarImport_General_ReadingCancelled;
                        ImportWindow.Dispose();
                        return false;
                    }
                case 1: // Successfull
                    {
                        monitor.StatusText = Properties.Resources.PolarImport_General_ReadingSucces;
                        ImportWindow.Dispose();
                        return PolarImportJobs.ProcessSonicLinkData(info, monitor, importResults, psmi);
                    }
                case 2: // Retry
                    {
                        monitor.StatusText = Properties.Resources.PolarImport_General_Retry;
                        // Call the import function recursive!
                        ImportWindow.Dispose();
                        return ImportUsingSonicLink(monitor, importResults, info, psg, psmi);
                    }
            }
            
            
            
            
            
            monitor.ErrorText = Properties.Resources.PolarImport_General_Not_implemented;
            ImportWindow.Dispose();
            return false;
        }


        #endregion

        #region Private members

        /// <summary>The unique GUID of this FitnessDevice</summary>
        private Guid id;

        /// <summary>The image to represent this FitnessDevice</summary>
        private System.Drawing.Image image;

        /// <summary>The name of this FitnessDevice</summary>
        private string name;

        private Settings.PolarDevice device;

        #endregion
    }
}
